package com.facebook.swift.codec.metadata;

import static com.google.common.base.Preconditions.checkState;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
import java.util.logging.Logger;

import javax.annotation.concurrent.Immutable;

import com.facebook.swift.codec.ThriftField.Requiredness;
import com.facebook.swift.codec.metadata.ThriftFieldMetadata;
import com.facebook.swift.codec.metadata.ThriftInjection;
import com.facebook.swift.codec.metadata.ThriftParameterInjection;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;

import net.gdface.utils.ClassCommentProvider;

/**
 * {@link ThriftFieldMetadata}的代理类，
 * 重载{@link #getRequiredness()}方法，根据参数类型对返回值进行修改
 * @author guyadong
 *
 */
@Immutable
public class DecoratorThriftFieldMetadata extends ThriftFieldMetadata {
	private static final Logger logger = Logger.getLogger(DecoratorThriftFieldMetadata.class.getName());
	
	/**
	 * {@link ClassCommentProvider}接口实例
	 * 当前字段不为{@code null}，且没有定义找到{@link com.facebook.swift.codec.ThriftDocumentation}定义的文档数据时,
	 * 使用javadoc从源码读取的注释作为文档数据
	 */
	private ClassCommentProvider javadocCommentProvider = null;
	/**
	 * 字段名称转换实例<br>
	 * 输入原始类型名<br>
	 * 输出转换后的字段名
	 */
	public static Function<String, String> fieldNameTransformer = null;
	/**
	 * 参数名称转换实例<br>
	 * 输入原始类型名<br>
	 * 输出转换后的参数名
	 */
	public static Function<String, String> parameterNameTransformer = null;
	public static Predicate<DecoratorThriftFieldMetadata> requirednessFilter = null;
	public static Function<DecoratorThriftFieldMetadata,String> sizeLimitAnnotationGetter = null;
	public static Function<DecoratorThriftFieldMetadata,Integer> preAllocsizeGetter = null;
	public static Function<DecoratorThriftFieldMetadata,String> defaultValueGetter = null;
	public static Function<DecoratorThriftFieldMetadata,String> typePrefixGetter = null;
	public static Predicate<DecoratorThriftFieldMetadata> pointerFilter = null;
	public static Predicate<DecoratorThriftFieldMetadata> preAllocFilter = null;
	public static Function<DecoratorThriftFieldMetadata,List<String>> documentAppender = null;
    private static Boolean primitiveOptional = null;
    private static Boolean objectOptional = null;
    private final Type javaType;
    private volatile ErpcType erpcType;
	private volatile ImmutableList<String> documentation;
	private Method ownedMethod;
	public DecoratorThriftFieldMetadata(ThriftFieldMetadata input){
        super(
                input.getId(),
                input.getRequiredness(),
                input.getThriftType(),
                input.getName(),
                input.getType(),
                input.getInjections(),
                input.getConstructorInjection(),
                input.getMethodInjection(),
                input.getExtraction(),
                input.getCoercion());
		// 获取field的类型
		List<ThriftInjection> injections = getInjections();
		checkState(injections.size()>0,"invalid size of injections");
		ThriftInjection injection = injections.get(0);		
		if(injection instanceof ThriftParameterInjection){
			javaType = ((ThriftParameterInjection)injection).getJavaType();
		}else if(injection instanceof ThriftFieldInjection){
			javaType = ((ThriftFieldInjection)injection).getField().getType();
		}else{
			javaType = null;
			// 对于不支持的数据类型无法获取field类型，输出警告
			logger.warning(
					String.format("UNSUPPORED TYPE %s，can't get Java Type. "
							+ "(不识别的ThriftInjection实例类型，无法实现requiredness转义)",
					null == injection? null : injection.getClass().getName()));
		}
	}
	
	/** 重载方法,实现 requiredness 转义 */
	@Override
	public Requiredness getRequiredness() {
		try{
			if(requirednessFilter != null ){
				return requirednessFilter.apply(this) ? Requiredness.REQUIRED : Requiredness.OPTIONAL;
			}
		}catch (RuntimeException e) {
			// DO NOTHING
			// 过滤器对象抛出异常则继续下面的逻辑
		}

		Requiredness requiredness = super.getRequiredness();
		checkState(Requiredness.UNSPECIFIED != requiredness);
		// 当为primitive类型时，Requiredness 为REQUIRED
		// 当为primitive类型的Object封装类型时(Long,Integer,Boolean)，Requiredness为OPTIONAL
		if(Boolean.TRUE.equals(objectOptional) && requiredness == Requiredness.NONE){
			if(javaType instanceof Class<?>)
			{
				Class<?> parameterClass = (Class<?>)javaType;
				if(parameterClass.isPrimitive()){
					requiredness = Requiredness.REQUIRED;
				}else {
					requiredness = Requiredness.OPTIONAL;
				}				
			}else{
				requiredness = Requiredness.OPTIONAL;
			}
		}else	if( !Boolean.FALSE.equals(primitiveOptional)
				&& javaType instanceof Class<?>
				&& requiredness == Requiredness.NONE){
			Class<?> parameterClass = (Class<?>)javaType;
			if(parameterClass.isPrimitive()){
				requiredness = Requiredness.REQUIRED;
				// logger.info(String.format("%s %s", parameterClass.getSimpleName(),requiredness));
			}else if(Primitives.isWrapperType(parameterClass)){
				requiredness = Requiredness.OPTIONAL;
				// logger.info(String.format("%s %s", parameterClass.getSimpleName(),requiredness));
			}
		}
		return requiredness;
	}
	
	@Override
	public ImmutableList<String> getDocumentation() {
		// double checking
		if(documentation == null){
			synchronized (this) {
				if(documentation == null){
					documentation = super.getDocumentation();
					if( javadocCommentProvider != null){
						if(documentation == null || documentation.isEmpty()){
							documentation = javadocCommentProvider.commentOfField(super.getName());
							if(documentAppender != null){
								documentation = ImmutableList.<String>builder()
										.addAll(documentation)
										.addAll(documentAppender.apply(this)).build();
							}
						}
					}
				}
			}
		}
		return documentation;
	}
    /**
	 * 设置primitiveOptional标记<br>
	 * 指定{@link #getRequiredness}方法调用时是否对primitive类型及其封装类型(Integer,Long)参数的返回值进行替换<br>
	 * 默认值:{@code true}<br>
	 * 该方法只能被调用一次
	 * @param optional
	 * @see #getRequiredness()
	 * @throws IllegalStateException 方法已经被调用
	 */
	public static synchronized void setPrimitiveOptional(boolean optional) {
		checkState(null == DecoratorThriftFieldMetadata.primitiveOptional,"primitiveOptional is initialized already.");
		DecoratorThriftFieldMetadata.primitiveOptional = optional;
	}
    /**
	 * 设置ObjectOptional标记<br>
	 * 指定{@link #getRequiredness}方法调用时是否对Object类型参数的返回值进行替换<br>
	 * 默认值:{@code true}<br>
	 * 该方法只能被调用一次
	 * @param optional
	 * @see #getRequiredness()
	 * @throws IllegalStateException 方法已经被调用
	 */
	public static synchronized void setObjectOptional(Boolean optional) {
		checkState(null == DecoratorThriftFieldMetadata.objectOptional,"ObjectOptional is initialized already.");
		DecoratorThriftFieldMetadata.objectOptional = optional;
	}
	public ErpcType getErpcType(){
		if(erpcType == null){
			synchronized (this) {
				if(erpcType == null){
					erpcType = ErpcType.getErpcType(getThriftType());
				}
			}
		}
		return erpcType;
	}
	/**
	 * @return javadocCommentProvider
	 */
	public ClassCommentProvider getJavadocCommentProvider() {
		return javadocCommentProvider;
	}
	/**
	 * @param javadocCommentProvider 要设置的 javadocCommentProvider
	 * @return 当前对象
	 */
	public DecoratorThriftFieldMetadata setJavadocCommentProvider(ClassCommentProvider javadocCommentProvider) {
		this.javadocCommentProvider = javadocCommentProvider;
		return this;
	}	
	
	/**
	 * @return ownedMethod
	 */
	public Method getOwnedMethod() {
		return ownedMethod;
	}
	/**
	 * @param ownedMethod 要设置的 ownedMethod
	 * @return 当前对象
	 */
	public DecoratorThriftFieldMetadata setOwnedMethod(Method ownedMethod) {
		this.ownedMethod = ownedMethod;
		return this;
	}
	
	public String getSizeLimit(){
		if(sizeLimitAnnotationGetter != null){
			return sizeLimitAnnotationGetter.apply(this);
		}
		return "";
	}
	public int getPreAllocSize(){
		if(preAllocsizeGetter != null){
			return preAllocsizeGetter.apply(this);
		}
		return -1;
	}
	public int getMaxSize(){
		int size = getPreAllocSize();
		if(size >0 && getErpcType().getProtocolType().isString()){
			return --size;
		}
		return size;
	}
	public String getDefaultValue(){
		if(defaultValueGetter != null){
			return defaultValueGetter.apply(this);
		}
		return null;
	}
	
	public String getTypePrefix(){
		if(typePrefixGetter != null){
			return typePrefixGetter.apply(this);
		}
		return "";
	}
	
	public boolean isPreAlloc(){
		if(preAllocFilter != null){
			return preAllocFilter.apply(this);
		}
		Class<?> rawType = TypeToken.of(getThriftType().getJavaType()).getRawType();
		if(rawType.isPrimitive() || rawType.isEnum()){
			return true;
		}
		return false;
	}
	public boolean isPointer(){
		if(pointerFilter != null){
			return pointerFilter.apply(this);
		}
		if(TypeToken.of(getThriftType().getJavaType()).isPrimitive()){
			return true;
		} 
		if(getRequiredness().equals(Requiredness.OPTIONAL)){
			return true;
		}
		if(getTypePrefix().contains("byref")){
			return true;
		}
		return false;
	}
	public boolean isPrimitive(){
		return TypeToken.of(getThriftType().getJavaType()).isPrimitive();
	}
	public boolean isCanPrimitive(){
		return TypeToken.of(getThriftType().getJavaType()).unwrap().getRawType().isPrimitive();
	}
	public boolean isEnum(){
		return TypeToken.of(getThriftType().getJavaType()).unwrap().getRawType().isEnum();
	}
	public String getMemberName() {
		return fieldNameTransformer != null 
				? fieldNameTransformer.apply(super.getName()) 
				: super.getName();
	}	
	public String getParameterName() {
		return parameterNameTransformer != null 
				? parameterNameTransformer.apply(super.getName()) 
				: super.getName();
	}
	
	private String renderField(boolean withConstant,boolean byRef,String name){
		if(isPreAlloc()){
			int size = getPreAllocSize();
			if(size > 0){
				String format = getErpcType().getPreAllocTypeFormat(size);
				return String.format(format, name);
			}
		}
		return String.format("%s %s",
				getErpcType().getCtype(withConstant,byRef),
				name);
	}
	
	public String renderMember(){
		return renderField(false,!isPreAlloc(),getMemberName());
	}
	public String renderParameter(){		
		return renderField(!isCanPrimitive() && !isEnum(),!isCanPrimitive() && !isEnum(),getName());
	}
}
